/*
 * @Author: wangxing wangxing@kd.com
 * @Date: 2025-05-07 11:38:07
 * @LastEditors: wangxing wangxing@kd.com
 * @LastEditTime: 2025-07-02 10:49:06
 * @FilePath: \quick-front\static\drawio\utils\mxOverHandler.js
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
/**
 * @description 处理元素、文本超过图框的情况
 */
function mxOverHandler(ui) {
  this.ui = ui;
  this.graph = ui.editor.graph;

  this.graph.setAllowNegativeCoordinates(false)

  // 1、限制实体元素的移动
  this.guideMove();

  // 2、限制关系线移动时，不能超出图框
  this.edgeMove();

  // 3、
  this.graph.model.addListener(mxEvent.CHANGE, (_model, mxEventObject) => {
    const valueChangeCells = mxEventObject.properties.changes
      .filter((i) => i instanceof mxValueChange)
      .map((i) => i.cell);
    // 3.1 处理文本超出
    this.labelOver(valueChangeCells);

    const geoChange = mxEventObject.properties.changes.filter((i) => i instanceof mxGeometryChange);
    if (geoChange.length) {
      geoChange.forEach((item) => {
        if (item.cell.requireInterface) {
          // 3.2 处理端口上的请求接口和所需接口调整重点位置时
          this.adjustEndPoint(item.cell);

          // 3.3 端口的请求接口和所需接口，调整位置时，自动更新label位置
          const targetPoint = item.cell.geometry.targetPoint;
          if (targetPoint) {
            item.cell.geometry.offset.x = targetPoint.x / 2;
            item.cell.geometry.offset.y = targetPoint.y / 2;
          }
        }
      });
    }
  });

  /**
   * 4、其他特殊处理：
   * multiUmlPosUpdate的moveCell方法
   * */
}

/**
 * @description 获取当前选中元素(包括子元素)顶点离图框的距离-即可移动的最大距离
 */
mxOverHandler.prototype.getSelectionBoundingBox = function() {
  const { graph } = this;
  const selectionBounds = graph.getBoundingBox(graph.getSelectionCells());
  if (selectionBounds) {
    const translate = graph.view.translate;
    const s = graph.view.scale;
    // 可移动的最大距离
    const maxMoveX = selectionBounds.x - translate.x * s;
    const maxMoveY = selectionBounds.y - translate.y * s;
    return (this.moveBoundingBoxCache = {
      x: maxMoveX,
      y: maxMoveY,
    });
  }
  this.moveBoundingBoxCache = null;
};

// 鼠标事件
mxOverHandler.prototype.mouseDown = function(sender, me) {};
mxOverHandler.prototype.mouseMove = function(sender, me) {};
mxOverHandler.prototype.mouseUp = mxOverHandler.prototype.getSelectionBoundingBox;

/**
 * 拦截元素的移动，限制元素不可超出边框
 */
mxOverHandler.prototype.guideMove = function() {
  const { graph } = this;
  const that = this;

  // 选中元素发生变化，获取元素的位置信息
  graph.getSelectionModel().removeListener(this.getSelectionBoundingBox);
  graph.getSelectionModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, this.getSelectionBoundingBox));
  graph.addMouseListener(this);

  const oldMove = mxGuide.prototype.move;
  mxGuide.prototype.move = function(bounds, delta, gridEnabled, clone) {
    const selectCells = graph.getSelectionCells();
    if (selectCells.length && that.moveBoundingBoxCache) {
      // 可移动的最大距离
      delta.x = Math.max(delta.x, -that.moveBoundingBoxCache.x);
      delta.y = Math.max(delta.y, -that.moveBoundingBoxCache.y);
    }

    return oldMove.call(this, bounds, delta, gridEnabled, clone);
  };
};

/**
 * 拦截关系线的移动，限制不可超出边框
 */
mxOverHandler.prototype.edgeMove = function() {
  // 选中元素发生变化，获取元素的位置信息
  const oldMove = mxEdgeHandler.prototype.mouseMove;
  mxEdgeHandler.prototype.mouseMove = function(graph, me) {
    if (null != this.index && null != this.marker) {
      this.currentPoint = this.getPointForEvent(me);
      const translate = graph.view.translate;
      const s = graph.view.scale;
      // 不能超出图框
      if (this.currentPoint.y - translate.y * s < 0) {
        me.graphY = translate.y;
      }
      if (this.currentPoint.x - translate.x * s < 0) {
        me.graphX = translate.x;
      }
    }
    return oldMove.call(this, graph, me);
  };
};

mxOverHandler.prototype.labelOver = function(cells) {
  const { graph } = this;
  if (cells.length) {
    const translate = graph.view.translate;
    const s = graph.view.scale;
    cells.forEach((cell) => {
      const bounds = graph.getBoundingBox([cell]);
      const overX = bounds.x - translate.x * s;
      if (overX < 0) {
        const spacingLeft = graph.getCellStyle(cell)[mxConstants.STYLE_SPACING_RIGHT] || 0;
        let styleString = graph.model.getStyle(cell);
        styleString = mxUtils.setStyle(styleString, mxConstants.STYLE_SPACING_RIGHT, spacingLeft + Math.ceil(overX));
        graph.model.setStyle(cell, styleString);
      }
    });
  }
};

/**
 * @description 检查请求接口等是否超出了，超出则自动调整
 */
mxOverHandler.prototype.adjustEndPoint = function(cell) {
  const { graph } = this;
  const translate = graph.view.translate;
  const s = graph.view.scale;
  const { x: boundX, y: boundY } = graph.getBoundingBox([cell]);
  const state = graph.view.getState(cell);
  const startPoint = {
    x: state.absolutePoints[0].x - translate.x * s,
    y: state.absolutePoints[0].y - translate.y * s,
  };
  const endPoint = {
    x: boundX - translate.x * s,
    y: boundY - translate.y * s,
  };

  if (endPoint.x >= 0 && endPoint.y >= 0) return endPoint;

  const { x: startX, y: startY } = startPoint;
  const { x: endX, y: endY } = endPoint;
  let dx = endX - startX;
  let dy = endY - startY;
  const getRadians = (angle) => (angle * Math.PI) / 180;

  const radians = Math.atan2(dy, dx);
  let degrees = radians * (180 / Math.PI);
  // 当前edge的角度
  degrees = degrees < 0 ? degrees + 360 : degrees;

  const radius = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
  let result = { ...endPoint };
  // 顺时针旋转，增加角度，直到在图框内
  for (let angle = degrees; angle < 360; angle += 5) {
    const x = startX + radius * Math.cos(getRadians(angle));
    const y = startY + radius * Math.sin(getRadians(angle));
    if (Math.floor(x) > 0 && Math.floor(y) > 0) {
      result.x = x;
      result.y = y;
      break;
    }
  }
  // 更新终点位置
  cell.geometry.targetPoint.x += result.x - endPoint.x;
  cell.geometry.targetPoint.y += result.y - endPoint.y;
};

mxOverHandler.prototype.destroy = function() {
  this.graph.getSelectionModel().removeListener(this.getSelectionBoundingBox);
  this.graph.removeMouseListener(this);
  this.moveBoundingBoxCache = null;
};
